#include <string.h>
#include "ota.h"
#include "flash_on_chip.h"
#include "uart.h"
static inline void send_ok(void) { HAL_UART_Transmit(&huart3, (uint8_t *)"OK\r\n", 4, 100); }
static inline void send_err(void) { HAL_UART_Transmit(&huart3, (uint8_t *)"ERR\r\n", 5, 100); }
static uint32_t crc32_full_at(uint32_t base, uint32_t size)
{
	uint32_t c = 0xFFFFFFFF;
	for (uint32_t i = 0; i < size; i++)
	{
		c ^= *(uint8_t *)(base + i);
		for (int j = 0; j < 8; j++)
		{
			c = (c & 1) ? (0xEDB88320u ^ (c >> 1)) : (c >> 1);
		}
	}
	return c ^ 0xFFFFFFFF;
}
static int meta_read(ota_flag_t *m)
{
	memcpy(m, (void *)Flage_PAGE_ADDR, sizeof(*m));
	return (m->magic == 0xB00710AD && m->committed == 0x5AA55AA5) ? 0 : -1;
}

static void meta_clear(void)
{
	HAL_FLASH_Unlock();
	FLASH_EraseInitTypeDef er = {0};
	uint32_t e = 0;
	er.TypeErase = FLASH_TYPEERASE_PAGES;
	er.PageAddress = Flage_PAGE_ADDR;
	er.NbPages = 1;
	HAL_FLASHEx_Erase(&er, &e);
	HAL_FLASH_Lock();
}


int start_ota(const ota_hdr_t *hdr)
{
	uint32_t total = 0;
	const uint32_t staging_limit = OTA_END_ADDR - OTA_START_ADDR - PAGE_SIZE; /* 预留最后页存 meta */
	total = hdr->total_size;
	int ret = 0;
	if (total == 0 || total > staging_limit)
	{
		send_err();
		return -102;
	}
	
	if ((ret = flash_erase(OTA_START_ADDR, OTA_SIZE)) != 0)
	{
		printf("flash_erase error:%d",ret);
		send_err();
		return -103;
	}
	send_ok();
	return 0;
}

int commit_ota(const ota_hdr_t *hdr)
{

	uint32_t dst = OTA_START_ADDR + hdr->offset;
	const uint8_t *data = hdr->chunk;
	int ret = flash_write(dst, data, hdr->chunk_len);
	if (ret != 0)
	{
		printf("flsh_wirte error ret %d\r\n", ret);
		send_err();
		return -106;
	}
	send_ok();
	return 0;
}

int verfy_ota_and_mark(const ota_hdr_t *hdr)
{
	uint8_t buf[20];
	flash_read(OTA_START_ADDR, buf, hdr->total_size);
	uint32_t rolling_crc = crc32_full_at(OTA_START_ADDR, hdr->total_size);
	printf("rolling_crc %08x\r\n", rolling_crc);
	if (hdr->offset != rolling_crc)
	{
		send_err();
		printf("crc32 error\r\n");
		return -1;
	}
	ota_flag_t m = {0xB00710ADu, hdr->total_size, rolling_crc, 0x5AA55AA5u};
	/* 擦除 meta 页并写入（半字编程） */
	HAL_FLASH_Unlock();
	FLASH_EraseInitTypeDef er = {0};
	uint32_t e = 0;
	er.TypeErase = FLASH_TYPEERASE_PAGES;
	er.PageAddress = Flage_PAGE_ADDR;
	er.NbPages = 1;
	HAL_FLASHEx_Erase(&er, &e);
	const uint8_t *pm = (const uint8_t *)&m;
	for (uint32_t i = 0; i < sizeof(m); i += 2)
	{
		uint16_t half = pm[i] | ((i + 1 < sizeof(m) ? pm[i + 1] : 0xFF) << 8);
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, Flage_PAGE_ADDR + i, half);
	}
	HAL_FLASH_Lock();
	printf("reboot system\r\n");
	NVIC_SystemReset();
	return 0;
}
void uart_ota_data(uint8_t *uart3_rx_buff, uint16_t size)
{
	const ota_hdr_t *hdr = (const ota_hdr_t *)uart3_rx_buff;
	printf("%d %d %d %d\r\n", hdr->cmd, hdr->total_size, hdr->offset + hdr->chunk_len, hdr->chunk_len);
	int rc = -1;
	if (hdr->cmd == 0x01)
	{
		printf("start_ota\n");
		rc = start_ota(hdr);
	}
	else if (hdr->cmd == 0x02)
	{
		printf("commit_ota\n");
		rc = commit_ota(hdr);
	}
	else if (hdr->cmd == 0x03)
	{
		rc = verfy_ota_and_mark(hdr);
	}
	else
	{
		send_err();
		printf("rc %d", rc);
		return;
	}

	// 如果 start_ota/commit_ota/verfy_ota_and_mark 没有自行发送 ACK，这里统一发
	if (rc == 0)
		send_ok();
	else
		send_err();
}


